﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace EasyNet.Componets.Core.Mapper
{
    internal class EasyNetMapperActivator
    {
        #region 生成映射表达式
        internal static Expression<Func<Tsource, Ttarget, Ttarget>> CreateMapActivater<Tsource, Ttarget>()
           where Tsource : class
           where Ttarget : class, new()
        {
            Type sourceType = typeof(Tsource), targetType = typeof(Ttarget);
            ParameterExpression sourceParam = Expression.Parameter(sourceType, "source");
            ParameterExpression targetParam = Expression.Parameter(targetType, "target");

            var paras = new List<ParameterExpression>();
            var exprs = new List<Expression>();
            ExchangeExpression(sourceParam, targetParam, sourceType, targetType, paras, exprs);

            exprs.Add(Expression.Label(Expression.Label(targetType), targetParam));

            BlockExpression body = Expression.Block(paras.ToArray(), exprs.ToArray());
            return Expression.Lambda<Func<Tsource, Ttarget, Ttarget>>(body, sourceParam, targetParam);
        }

        /// <summary>
        ///     不进行深度 映射
        /// </summary>
        /// <returns></returns>
        private static void ExchangeExpression(Expression sourceObj, Expression targetObj, Type sourceType,
            Type targetType, List<ParameterExpression> paras, List<Expression> exprs)
        {
            //获取 目标对象类型的  值属性 跟 字符串属性  的成员属性
            PropertyInfo[] targetProps = targetType.GetProperties(BindingFlags.Public | BindingFlags.Instance);
            //获取 源对象类型的  值属性跟 字符串属性  的成员属性
            PropertyInfo[] sourceProps = sourceType.GetProperties(BindingFlags.Public | BindingFlags.Instance);
            //查找 目标对象类型 跟 源对象类型 中名称相同 的共同的成员属性
            PropertyInfo[] props = targetProps.Where(p => sourceProps.Any(p1 => p1.Name == p.Name)).ToArray();

            int index = 0;
            //遍历共同的成员属性
            foreach (PropertyInfo tProperty in props)
            {
                //目标成员属性中过滤带有自定义不匹配属性的成员属性
                if (GetAttribute<EasyNetMapperIgnoreAttribute>(tProperty) != null) continue;
                //查找 源对象中的该成员属性
                PropertyInfo sProperty = sourceProps.First(p => p.Name == tProperty.Name);
                //如果目标成员属性不支持写  或者 源成员属性不支持读， 放弃对该成员属性的匹配值操作
                if (!tProperty.CanWrite || !sProperty.CanRead) continue;

                MemberExpression sourceProValue = Expression.Property(sourceObj, tProperty.Name);
                MemberExpression targetProValue = Expression.Property(targetObj, tProperty.Name);
                //如果成员属性的 类型 相同
                if (tProperty.PropertyType == sProperty.PropertyType)
                {
                    //成员属性值不相等，则赋值
                    exprs.Add(Expression.IfThen(
                        Expression.NotEqual(targetProValue, sourceProValue),
                        Expression.Assign(targetProValue, sourceProValue)
                        ));
                }
                //如果成员属性类型相同，但是命名空间不同，并且非泛型，采用递归映射
                else if (tProperty.PropertyType.Name == sProperty.PropertyType.Name
                    && !tProperty.PropertyType.IsGenericType)
                {
                    //生成一个 创建新对象并对成员属性赋值的 表达式
                    var condition = NewMemberExpress(sProperty.PropertyType, tProperty.PropertyType, sourceProValue);

                    exprs.Add(Expression.Assign(targetProValue, condition));
                }
                else
                {
                    Expression outTargetExp = null;
                    Expression outSourceExp = null;
                    ParameterExpression outParam = null;
                    ConvertValueTypeExpress(sProperty.PropertyType, tProperty.PropertyType, sourceProValue, targetProValue,
                        index++, out outSourceExp, out outTargetExp, out outParam);
                    if (outSourceExp != null && outTargetExp != null)
                    {
                        paras.Add(outParam);
                        exprs.Add(outSourceExp);
                        exprs.Add(outTargetExp);
                    }
                }
            }
        }

        #region 值对象相互转换

        private static void ConvertValueTypeExpress(Type sourceType, Type targetType, Expression sourceExpress, Expression targetProValue, int index,
            out Expression outSourceExp, out Expression outTargetExp, out ParameterExpression outParam)
        {
            //构造变量参数
            outSourceExp = null;
            outTargetExp = null;
            outParam = Expression.Variable(targetType, "var" + index);
            var sNullUnderlyingType = Nullable.GetUnderlyingType(sourceType);
            var tNullUnderlyingType = Nullable.GetUnderlyingType(targetType);
            bool sIsIntType = IsIntType(sourceType);
            bool tIsIntType = IsIntType(targetType);
            bool sIsFloateType = IsFloateType(sourceType);
            bool tIsFloateType = IsFloateType(targetType);
            bool sIsBoolOrEnum = IsBoolOrEnumType(sourceType);
            bool tIsBoolOrEnum = IsBoolOrEnumType(targetType);

            //如果 目标成员属性 为 源成员属性的 可空类型
            if (sNullUnderlyingType != null && ((sNullUnderlyingType == targetType)
                || (IsIntType(sNullUnderlyingType) && tIsIntType)
                || (IsFloateType(sNullUnderlyingType) && tIsFloateType)
                || (IsBoolOrEnumType(sNullUnderlyingType) && tIsIntType)
                || (IsIntType(sNullUnderlyingType) && tIsBoolOrEnum)))
            {
                outSourceExp = Expression.Assign(outParam,
                    Expression.Condition(
                        Expression.NotEqual(sourceExpress, Expression.Default(sourceType)),
                        Expression.Convert(sourceExpress, targetType),
                        Expression.Default(targetType)));
                outTargetExp = Expression.IfThen(
                    Expression.NotEqual(targetProValue, outParam),
                    Expression.Assign(targetProValue, outParam)
                    );
            }
            //如果 源成员属性的 为 目标成员属性 可空类型
            else if (tNullUnderlyingType != null && ((tNullUnderlyingType == sourceType)
                || (IsIntType(tNullUnderlyingType) && sIsIntType)
                || (IsFloateType(tNullUnderlyingType) && sIsFloateType)
                || (IsBoolOrEnumType(tNullUnderlyingType) && sIsIntType)
                || (IsIntType(tNullUnderlyingType) && sIsBoolOrEnum)))
            {
                outSourceExp = Expression.Assign(outParam, Expression.Convert(sourceExpress, targetType));
                outTargetExp = Expression.IfThen(
                    Expression.NotEqual(targetProValue, outParam),
                    Expression.Assign(targetProValue, outParam)
                    );
            }
            //int long sbyte 相互转换 
            //decimal floate double 相互转换
            // bool enum 数值 类型相互转换
            else if ((sIsIntType && tIsIntType)
                || (sIsFloateType && tIsFloateType)
                || (sIsBoolOrEnum && tIsIntType)
                || (tIsBoolOrEnum && sIsIntType)
                )
            {
                outSourceExp = Expression.Assign(outParam,
                    Expression.Convert(sourceExpress, targetType));
                outTargetExp = Expression.IfThen(
                    Expression.NotEqual(targetProValue, outParam),
                    Expression.Assign(targetProValue, outParam)
                    );
            }
        }


        private static bool IsIntType(Type type)
        {
            return type == null ? false : (type == typeof(sbyte) || type == typeof(byte) || type == typeof(int) || type == typeof(long));
        }

        private static bool IsFloateType(Type type)
        {
            return type == null ? false : (type == typeof(decimal) || type == typeof(float) || type == typeof(double));
        }

        private static bool IsBoolOrEnumType(Type type)
        {
            return type == null ? false : (type == typeof(bool) || type.IsEnum);
        }
        #endregion

        #endregion

        #region 生成对象并映射表达式
        public static Expression<Func<Tsource, Ttarget>> CreateMapNewActivater<Tsource, Ttarget>()
          where Tsource : class
          where Ttarget : class, new()
        {
            Type sourceType = typeof(Tsource), targetType = typeof(Ttarget);
            ParameterExpression sourceParam = Expression.Parameter(sourceType, "source");
            NewExpression targetObj = Expression.New(targetType);
            IEnumerable<MemberBinding> exprs = MemberBindExpression(sourceParam, targetObj, sourceType, targetType);

            return Expression.Lambda<Func<Tsource, Ttarget>>(Expression.MemberInit(targetObj, exprs.ToArray()), sourceParam);
        }

        /// <summary>
        ///     不进行深度 映射
        /// </summary>
        /// <returns></returns>
        private static IEnumerable<MemberBinding> MemberBindExpression(Expression sourceObj,
            Expression targetObj, Type sourceType, Type targetType)
        {
            //获取 目标对象类型的  值属性 跟 字符串属性  的成员属性
            PropertyInfo[] targetProps = targetType.GetProperties(BindingFlags.Public | BindingFlags.Instance);
            //获取 源对象类型的  值属性跟 字符串属性  的成员属性
            PropertyInfo[] sourceProps = sourceType.GetProperties(BindingFlags.Public | BindingFlags.Instance);
            //查找 目标对象类型 跟 源对象类型 中名称相同 的共同的成员属性
            PropertyInfo[] props = targetProps.Where(p => sourceProps.Any(p1 => p1.Name == p.Name)).ToArray();

            //遍历共同的成员属性
            foreach (PropertyInfo tProperty in props)
            {
                //目标成员属性中过滤带有自定义不匹配属性的成员属性
                if (GetAttribute<EasyNetMapperIgnoreAttribute>(tProperty) != null) continue;
                //查找 源对象中的该成员属性
                PropertyInfo sProperty = sourceProps.First(p => p.Name == tProperty.Name);
                //如果目标成员属性不支持写  或者 源成员属性不支持读， 放弃对该成员属性的匹配值操作
                if (!tProperty.CanWrite || !sProperty.CanRead) continue;

                MemberExpression sourceProValue = Expression.Property(sourceObj, tProperty.Name);
                //如果成员属性的 类型 相同
                if (tProperty.PropertyType == sProperty.PropertyType)
                {
                    yield return Expression.Bind(tProperty, sourceProValue);
                }
                //如果成员属性类型相同，但是命名空间不同，并且非泛型，采用递归映射
                else if (tProperty.PropertyType.Name == sProperty.PropertyType.Name
                    && !tProperty.PropertyType.IsGenericType)
                {
                    //生成一个 创建新对象并对成员属性赋值的 表达式
                    var condition = NewMemberExpress(sProperty.PropertyType, tProperty.PropertyType, sourceProValue);

                    yield return Expression.Bind(tProperty, condition);

                }
                else
                {
                    var valueExpress = ConvertValueTypeExpress(sProperty.PropertyType, tProperty.PropertyType, sourceProValue);
                    if (valueExpress != null)
                    {
                        yield return Expression.Bind(tProperty, valueExpress);
                    }
                }
            }
        }

        private static T GetAttribute<T>(MemberInfo memberInfo)
           where T : Attribute
        {
            object[] attrs = memberInfo.GetCustomAttributes(typeof(T), true);
            if (attrs == null || attrs.Length == 0)
                return null;
            return (T)attrs[0];
        }

        /// <summary>
        /// 生成一个 创建新对象并对成员属性赋值的 表达式
        /// </summary>
        /// <param name="sourceType">源类型</param>
        /// <param name="targetType">目标类型</param>
        /// <param name="sourceExpress">源属性表达式</param>
        /// <returns>创建目标类型对象并对成员属性赋值的 表达式</returns>
        private static ConditionalExpression NewMemberExpress(Type sourceType, Type targetType, Expression sourceExpress)
        {
            NewExpression innerTarget = Expression.New(targetType);
            IEnumerable<MemberBinding> innerExprs = MemberBindExpression(sourceExpress, innerTarget, sourceType, targetType);
            var member = Expression.MemberInit(innerTarget, innerExprs.ToArray());

            var condition = Expression.Condition(
                        Expression.NotEqual(sourceExpress, Expression.Default(sourceType)),
                        member,
                        Expression.Default(targetType));

            return condition;
        }

        private static Expression ConvertValueTypeExpress(Type sourceType, Type targetType, Expression sourceExpress)
        {
            var sNullUnderlyingType = Nullable.GetUnderlyingType(sourceType);
            var tNullUnderlyingType = Nullable.GetUnderlyingType(targetType);
            bool sIsIntType = IsIntType(sourceType);
            bool tIsIntType = IsIntType(targetType);
            bool sIsFloateType = IsFloateType(sourceType);
            bool tIsFloateType = IsFloateType(targetType);
            bool sIsBoolOrEnum = IsBoolOrEnumType(sourceType);
            bool tIsBoolOrEnum = IsBoolOrEnumType(targetType);

            //如果 目标成员属性 为 源成员属性的 可空类型
            if (sNullUnderlyingType != null && ((sNullUnderlyingType == targetType)
                || (IsIntType(sNullUnderlyingType) && tIsIntType)
                || (IsFloateType(sNullUnderlyingType) && tIsFloateType)
                || (IsBoolOrEnumType(sNullUnderlyingType) && tIsIntType)
                || (IsIntType(sNullUnderlyingType) && tIsBoolOrEnum)))
            {
                return Expression.Condition(Expression.NotEqual(sourceExpress, Expression.Constant(null)),
                        Expression.Convert(sourceExpress, targetType),
                        Expression.Default(targetType)
                        );
            }
            //如果 源成员属性的 为 目标成员属性 可空类型
            else if (tNullUnderlyingType != null && ((tNullUnderlyingType == sourceType)
                || (IsIntType(tNullUnderlyingType) && sIsIntType)
                || (IsFloateType(tNullUnderlyingType) && sIsFloateType)
                || (IsBoolOrEnumType(tNullUnderlyingType) && sIsIntType)
                || (IsIntType(tNullUnderlyingType) && sIsBoolOrEnum)))
            {
                return Expression.Convert(sourceExpress, targetType);
            }
            //int long sbyte 相互转换 
            //decimal floate double 相互转换
            // bool enum 数值 类型相互转换
            else if ((sIsIntType && tIsIntType)
                || (sIsFloateType && tIsFloateType)
                || (sIsBoolOrEnum && tIsIntType)
                || (tIsBoolOrEnum && sIsIntType)
                )
            {
                return Expression.Convert(sourceExpress, targetType);
            }

            return null;
        }
        #endregion
    }
}
